package com.unc.allergyex;

import java.util.ArrayList;
import java.util.List;

import android.app.AlertDialog;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ZoomControls;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.unc.allergyex.helpers.ObjManager;
import com.unc.allergyex.objects.MapRestaurant;
import com.unc.allergyex.objects.RestaurantItemizedOverlay;
import com.unc.allergyex.objects.RestaurantPin;
import com.unc.allergyex.objects.db.Restaurant;
import com.unc.allergyex.objects.db.RestaurantLocation;
import com.unc.allergyex.objects.tasks.MenuQueryTask;

/**
 *  This class is a map activity that implements a location listener.
 * It uses the network provider's location services which uses less battery
 * than GPS. The user can search for locations in the search bar, which uses
 * Android's GeoCoder. It displays all restaurants within 20 miles
 * of the current location or current search query.
 * 
 * It requires internet access and location services to be fully
 * functional, but it displays an alert dialog box if either of these
 * services are not enabled.
 * 
 * Its layout file is mapsearch.xml
 * @author Abigail
 *
 */
public class MapSearch extends MapActivity implements LocationListener{
	private MapView mapView;
	private List<Overlay> mapOverlays;
	private MapController mc;
	private Geocoder gc;
	private int lat;
	private int lon;
	private ArrayList<MapRestaurant> locations;
	private MenuQueryTask task;

	/**
	 * This method initializes the MapSearch class. It dynamically creates
	 * zoom controls. It checks for location updates from the network
	 * provider. If the network provider isn't activated it displays an alert
	 * dialog, but it still launches the activity so that it's got the search function.
	 * It gets the list of all restaurants from ObjectManager.
	 * 
	 * @see com.google.android.maps.MapActivity#onCreate(android.os.Bundle)
	 */
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.mapsearch);
		mapView = (MapView) findViewById(R.id.mapview);

		//set zoom controls
		ZoomControls zoomControls = (ZoomControls) findViewById(R.id.zoomcontrols);
		zoomControls.setOnZoomInClickListener(new OnClickListener() {
			public void onClick(View v) {
				mapView.getController().zoomIn();
			}
		});
		zoomControls.setOnZoomOutClickListener(new OnClickListener() {
			public void onClick(View v) {
				mapView.getController().zoomOut();
			}
		});

		mc = mapView.getController();
		mapOverlays = mapView.getOverlays();


		LocationManager mlocManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);

		mlocManager.requestLocationUpdates( LocationManager.NETWORK_PROVIDER, 0, 0, this);

		mlocManager.removeUpdates(this);

		Restaurant[] curr_restaurants = ObjManager.getMapSearchRestaurantList();
		locations = new ArrayList<MapRestaurant>();
		for (Restaurant r : curr_restaurants){
			for (RestaurantLocation l : r.getLocations()){
				MapRestaurant current = new MapRestaurant(r.getId(), 
						r.getName(), 
						l.getAddress(), 
						l.getLatitude(), 
						l.getLongitude(),
						l.getPhone());
				locations.add(current);
			}
		}

		Location curr_loc = mlocManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
		if (curr_loc == null || isOnline() == false){
			AlertDialog.Builder builder = new AlertDialog.Builder(this);
			builder.setMessage("AllergyEx can't display your current location. Please enable location services on your device and ensure you have network connectivity.");
			builder.setCancelable(false);
			builder.setPositiveButton("Okay", null);
			AlertDialog alert = builder.create();
			alert.show();
		}
		else{
			newLocation(curr_loc);
		}
		mc.setZoom(14);
		
		//keylistener for the edittext makes enter keypresses perform the search
		EditText input = (EditText) findViewById(R.id.mapsearchbar);
		input.setOnKeyListener(new View.OnKeyListener() {
	        public boolean onKey(View v, int keyCode, KeyEvent event) {
	        	//if it was enter and it was a keyup, go for the query
	            if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction()==0) {
	            	goToPoint(v);
	                return true;
	            }
	            return false;
	        }
	    });

	}
	/**
	 * 
	 * Adds a red mappin to the map where the current location is.
	 * Makes the map centered around the current location
	 * Calls displayNearby(loc) to display all restaurants within 20 miles of 
	 * the current location.
	 * 
	 * @param loc Location that is to be moved to
	 */
	public void newLocation(Location loc){
		//goes to a new location when location changes
		mapOverlays.clear();
		GeoPoint point = new GeoPoint((int)(loc.getLatitude() * 1E6), (int)(loc.getLongitude() * 1E6));

		MapRestaurant fake_rest = new MapRestaurant("", "Your Location", "",loc.getLatitude(), loc.getLongitude(), "");
		
		Drawable icon = getResources().getDrawable(R.drawable.mappin);
		int w = icon.getIntrinsicWidth();
		int h = icon.getIntrinsicHeight();
		icon.setBounds(-w / 2, -h, w / 2, 0);
		
		RestaurantItemizedOverlay itov = new RestaurantItemizedOverlay(icon, mapView, this);
		RestaurantPin item = new RestaurantPin(point, "", "hide", fake_rest);

		item.setMarker(icon);

		itov.addOverlay(item);

		mapOverlays.add(itov);
		mc.animateTo(point);
		displayNearby(point);
	}
	
	/**
	 * This method executes a menu query for the restaurant that has been selected.
	 * It is launched when the forward arrow is tapped on the restaurant pin's popup balloon.
	 * 
	 * @param v the balloon that contains the button that is being tapped
	 */
	public void restaurantSelected (View v){
		//launches when forward button is tapped
		View vc = ((ViewGroup)v.getParent()).findViewById(R.id.restaurantId);
		String id = ((TextView)vc).getText().toString();
		task = new MenuQueryTask();
		task.setContext(this);
		task.execute(id);
	}
	
	
	/**
	 * This method navigates to a new location when an address
	 * is entered into the search bar and the search button is tapped. It uses the Android
	 * GeoCoder which throws an exception if no address is found.
	 * If an exception is thrown, a toast is shown telling the user
	 * that their location was not found. No change is made to the screen.
	 * 
	 * @param v This is the search button view that launches this method
	 */
	public void goToPoint(View v){
		//called by search bar
		//move map to a new point
		mapOverlays.clear();
		final EditText et = (EditText) findViewById(R.id.mapsearchbar);
		String query = et.getText().toString();
		gc = new Geocoder(this);
		try{
			List<Address> foundAddresses = gc.getFromLocationName(query, 5);
			
			for (int i = 0; i < foundAddresses.size(); ++i) {
				Address x = foundAddresses.get(i);
				lat = (int) (x.getLatitude() * 1000000);
				lon = (int) (x.getLongitude()* 1000000);
			}
			
			GeoPoint thePoint = new GeoPoint(lat, lon);
			MapRestaurant fake_rest = new MapRestaurant("", "You are here", "",lat, lon, "");

			Drawable icon = getResources().getDrawable(R.drawable.mappin);
			int w = icon.getIntrinsicWidth();
			int h = icon.getIntrinsicHeight();
			icon.setBounds(-w / 2, -h, w / 2, 0);

			RestaurantItemizedOverlay itov = new RestaurantItemizedOverlay(icon, mapView, this);
			RestaurantPin item = new RestaurantPin(thePoint, "", "hide", fake_rest);

			item.setMarker(icon);
			itov.addOverlay(item);
			mapOverlays.add(itov);

			mc.animateTo(thePoint); //display the found address
			mc.setZoom(14);
			this.displayNearby(thePoint);

		}
		catch (Exception e){
			Toast.makeText(this, "Location not found. Please try again.", Toast.LENGTH_LONG);
		}


	}
	
	/**
	 * This method searches the list of current restaurant locations and
	 * displays all restaurants within a 20 mile radius from the current location.
	 * 
	 * @param loc This is the location that is the current red map pin 
	 */
	private void displayNearby(GeoPoint loc){
		
		double currlat = loc.getLatitudeE6();
		double currlon = loc.getLongitudeE6();
		
		for (int i = 0; i < locations.size(); i ++){
			
			if ( Math.abs(locations.get(i).getLat() - currlat) < 289855 && Math.abs(locations.get(i).getLon() - currlon) < 289855){
				
				Drawable icon = getResources().getDrawable(R.drawable.mappinblue);
				int w = icon.getIntrinsicWidth();
				int h = icon.getIntrinsicHeight();
				icon.setBounds(-w / 2, -h, w / 2, 0);

				RestaurantPin item = new RestaurantPin(new GeoPoint((int)(locations.get(i).getLat()), (int)(locations.get(i).getLon())), "", "", locations.get(i));
				RestaurantItemizedOverlay itov = new RestaurantItemizedOverlay(icon, mapView, this);

				item.setMarker(icon);
				itov.addOverlay(item);
				mapOverlays.add(itov);
			}
		}
	}

	/**
	 * Required method for MapActivity. We're not using it.
	 * @see com.google.android.maps.MapActivity#isRouteDisplayed()
	 */
	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}

	/**
	 * Default method required for Location Listener.
	 * Calls newLocation(loc) for the new location.
	 * @see android.location.LocationListener#onLocationChanged(android.location.Location)
	 */
	@Override
	public void onLocationChanged(Location loc) {
		this.newLocation(loc);
	}
	
	/**
	 * Default method required for Location Listener. We aren't using it
	 * because we have a check for the provider being disabled in the onCreate() method.
	 * 
	 * @see android.location.LocationListener#onProviderDisabled(java.lang.String)
	 */
	@Override
	public void onProviderDisabled(String provider){
	}

	/**
	 * Default method required for LocationListener. We aren't using it.
	 * 
	 * @see android.location.LocationListener#onProviderEnabled(java.lang.String)
	 */
	@Override
	public void onProviderEnabled(String provider){
		
	}

	
	/**
	 * Default method required for LocationListener. We aren't using it.
	 * @see android.location.LocationListener#onStatusChanged(java.lang.String, int, android.os.Bundle)
	 */
	@Override
	public void onStatusChanged(String provider, int status, Bundle extras){
		
	}
	/**
	 * Back button that finishes the activity
	 * 
	 * @param v default param for this method
	 */
	public void returnButtonClicked(View v){
		//back button method
		this.finish();
	}
	/**
	 * Checks to see if device has Internet connectivity.
	 * Requires ACCESS_NETWORK_STATE permission in manifest file
	 * 
	 * @return boolean value indicating online status
	 */
	private boolean isOnline() {
		ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo netInfo = cm.getActiveNetworkInfo();
		if (netInfo != null && netInfo.isConnectedOrConnecting()) {
			return true;
		}
		return false;
	}
	
	/**cancel the query task when the activity is paused
	 * @see android.app.Activity#onPause()
	 */
	@Override
	public void onPause(){
		super.onPause();
		cancelTask();
	}
	
	/**cancel the query task when the activity is destroyed
	 * @see android.app.Activity#onDestroy()
	 */
	@Override
	public void onDestroy(){
		super.onDestroy();
		cancelTask();
	}
	
	/**cancel the query task if it is not null
	 * 
	 */
	private void cancelTask(){
		if (task != null){
			task.cancel(true);
		}
	}

}


